﻿using JetBrains.Annotations;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Dotnet.Utils.Utility.ObjectExtensions.Number
{
    public static class RandomUtil
    {
        private static readonly Random Rnd = new Random();

        /// <summary>
        /// Returns a random number within a specified range.
        /// </summary>
        /// <param name="minValue">The inclusive lower bound of the random number returned.</param>
        /// <param name="maxValue">The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue.</param>
        /// <returns>
        /// A 32-bit signed integer greater than or equal to minValue and less than maxValue;
        /// that is, the range of return values includes minValue but not maxValue.
        /// If minValue equals maxValue, minValue is returned.
        /// </returns>
        public static int GetRandom(int minValue, int maxValue)
        {
            lock (Rnd)
            {
                return Rnd.Next(minValue, maxValue);
            }
        }

        /// <summary>
        /// Returns a nonnegative random number less than the specified maximum.
        /// </summary>
        /// <param name="maxValue">The exclusive upper bound of the random number to be generated. maxValue must be greater than or equal to zero.</param>
        /// <returns>
        /// A 32-bit signed integer greater than or equal to zero, and less than maxValue;
        /// that is, the range of return values ordinarily includes zero but not maxValue.
        /// However, if maxValue equals zero, maxValue is returned.
        /// </returns>
        public static int GetRandom(int maxValue)
        {
            lock (Rnd)
            {
                return Rnd.Next(maxValue);
            }
        }

        /// <summary>
        /// Returns a nonnegative random number.
        /// </summary>
        /// <returns>A 32-bit signed integer greater than or equal to zero and less than <see cref="int.MaxValue"/>.</returns>
        public static int GetRandom()
        {
            lock (Rnd)
            {
                return Rnd.Next();
            }
        }

        /// <summary>
        /// Gets random of given objects.
        /// </summary>
        /// <typeparam name="T">Type of the objects</typeparam>
        /// <param name="objs">List of object to select a random one</param>
        public static T GetRandomOf<T>([NotNull] params T[] objs)
        {
            Check.NotNullOrEmpty(objs, nameof(objs));

            return objs[GetRandom(0, objs.Length)];
        }

        /// <summary>
        /// Gets random item from the given list.
        /// </summary>
        /// <typeparam name="T">Type of the objects</typeparam>
        /// <param name="list">List of object to select a random one</param>
        public static T GetRandomOfList<T>([NotNull] IList<T> list)
        {
            Check.NotNullOrEmpty(list, nameof(list));

            return list[GetRandom(0, list.Count)];
        }

        /// <summary>
        /// Generates a randomized list from given enumerable.
        /// </summary>
        /// <typeparam name="T">Type of items in the list</typeparam>
        /// <param name="items">items</param>
        public static List<T> GenerateRandomizedList<T>([NotNull] IEnumerable<T> items)
        {
            Check.NotNull(items, nameof(items));

            var currentList = new List<T>(items);
            var randomList = new List<T>();

            while (currentList.Any())
            {
                var randomIndex = GetRandom(0, currentList.Count);
                randomList.Add(currentList[randomIndex]);
                currentList.RemoveAt(randomIndex);
            }

            return randomList;
        }

        /// <summary>
        /// 生成真正的随机数
        /// </summary>
        /// <param name="rand"></param>
        /// <param name="maxValue"></param>
        /// <returns></returns>
        // ReSharper disable once UnusedParameter.Global
#pragma warning disable IDE0060

        public static int StrictNext(this Random rand, int maxValue = int.MaxValue)
#pragma warning restore IDE0060
        {
            return new Random(BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0)).Next(maxValue);
        }

        /// <summary>
        /// 产生正态分布的随机数
        /// </summary>
        /// <param name="rand"></param>
        /// <param name="mean">均值</param>
        /// <param name="stdDev">方差</param>
        /// <returns></returns>
        public static double NextGauss(this Random rand, double mean, double stdDev)
        {
            var u1 = 1.0 - rand.NextDouble();
            var u2 = 1.0 - rand.NextDouble();
            var randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2);
#pragma warning disable IDE0048
            return mean + stdDev * randStdNormal;
#pragma warning restore IDE0048
        }

        public static int Next()
        {
            return Random.Next();
        }

        public static int Next(int maxValue)
        {
            return Random.Next(maxValue);
        }

        public static int Next(int minValue, int maxValue)
        {
            return Random.Next(minValue, maxValue);
        }

        public static double NextDouble()
        {
            return Random.NextDouble();
        }

        public static string NextString(int length)
        {
            char[] array = new char[length];
            for (int i = 0; i < length; i++)
            {
                int index = Random.Next("123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ~!@#$%^&*()_+|".Length);
                array[i] = "123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ~!@#$%^&*()_+|"[index];
            }
            return string.Join("", array);
        }

        public static string GenerateString(int maxLength, string text = null)
        {
            if (text == null)
            {
                text = "123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ~!@#$%^&*()_+|";
            }
            StringBuilder stringBuilder = new StringBuilder();
            int randomLength = GetRandomLength(maxLength);
            for (int i = 0; i < randomLength; i++)
            {
                stringBuilder.Append(GetRandomChar(text));
            }
            return stringBuilder.ToString();
        }

        private static int GetRandomLength(int maxLength)
        {
            return Next(1, maxLength);
        }

        private static string GetRandomChar(string text)
        {
            return text[Next(1, text.Length)].ToString();
        }

        private const string CharSet = "123456789abcdefghijklmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ~!@#$%^&*()_+|";

        private static readonly Random Random = new Random(DateTime.Now.Millisecond);
    }
}